#define _CRT_SECURE_NO_WARNINGS 1
#include "Snake.h"

//设置光标位置
void GetPos(short x,short y)
{
	COORD POS = { 0 };
	POS.X = x;
	POS.Y = y;
	//获取句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(houtput, POS);
}

//隐藏光标
void HideCursor()
{
	//获取句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//获取该句柄所对应的光标信息
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(houtput, &CursorInfo);
	//隐藏光标
	CursorInfo.bVisible = false;
	//将设置完的光标信息返回给控制台
	SetConsoleCursorInfo(houtput, &CursorInfo);
}

//打印欢迎界面
void welcome()
{
	GetPos(54, 16);
	printf("贪吃蛇");
	GetPos(44, 30);
	system("pause");
	system("cls");
	GetPos(44, 16);
	printf("用 ↑. ↓ . ← . → 来控制蛇的移动");
	GetPos(44, 17);
	printf("用shift加速，用ctrl减速");
	GetPos(44, 18);
	printf("加速可以获得更高的分数");
	GetPos(44, 30);
	system("pause");
	system("cls");
}

void CreateMap()
{
	//打印上墙
	GetPos(0, 0);
	int i = 0;
	while (i < 34)
	{
		wprintf(L"%lc ", WALL);
		i++;
	}
	//打印下墙
	GetPos(0, 38);
	i = 0;
	while (i < 34)
	{
		wprintf(L"%lc ", WALL);
		i++;
	}
	//打印左墙
	for (i = 1; i <= 38; i++)
	{
		GetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//打印右墙
	for (i = 0; i <= 38; i++)
	{
		GetPos(68, i);
		wprintf(L"%lc", WALL);
	}
}

//初始化蛇
void SnakeInit(pSnake ps)
{
	ps->_pSnake = NULL;
	//将蛇的头部初始化为（20,5）
	//创建蛇头（头节点）
	pSnakeNode cur = NULL;

	//初始化蛇身
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		//申请5个节点作为蛇身
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("SnakeInit::malloc");
			return;
		}
		cur->next = NULL;
		//设置蛇身坐标
		cur->x = POS_X + 2 * i;
		cur->y = POS_Y;

		//创建好节点后，头插到蛇头处
		if (ps->_pSnake == NULL)//链表为空
		{
			ps->_pSnake = cur;
		}
		else//非空链表
		{
			cur->next = ps->_pSnake;
			ps->_pSnake = cur;
		}
	}
	//现在cur就是蛇头
	cur = ps->_pSnake;
	//打印蛇
	while (cur) 
	{
		GetPos(cur->x, cur->y);
		wprintf(L"%lc",BODY);
		cur = cur->next;
	}
	//设置蛇的基本信息
	ps->_curstate = OK;
	ps->_dir = RIGHT;
	ps->_foodwelth = 10;
	ps->_score = 0;
	ps->_sleep = 200;

}

//初始化食物
void FoodInit(pSnake ps)
{
	int x = 0;
	int y = 0;
again:
	do
	{
		x = rand() % 65 + 1;
		y = rand() % 37 + 1;
	} while(x%2 != 0);
	//食物不能和蛇的身体冲突
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}
	//创建食物节点
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("FoodInit()::malloc()");
		return;
	}
	food->x = x;
	food->y = y;

	GetPos(food->x, food->y);
	wprintf(L"%lc", FOOD);

	ps->_pFood = food;
}

//游戏初始化
void GameInit(pSnake ps)
{
	//设置控制台窗口大小
	system("mode con cols=120 lines=40");
	//设置控制台名称
	system("title 贪吃蛇");
	//隐藏光标
	HideCursor();
	//打印欢迎界面
	welcome();
	//创建地图
	CreateMap();
	//初始化蛇
	SnakeInit(ps);
	//初始化食物
	FoodInit(ps);
}

//打印帮助信息
void Print(pSnake ps)
{
	GetPos(78, 21);
	printf("用 ↑. ↓ . ← . → 来控制蛇的移动");
	GetPos(78, 22);
	printf("用shift加速，用ctrl减速");
	GetPos(78, 23);
	printf("加速可以获得更高的分数");
	GetPos(78, 24);
	printf("ESC退出游戏，SPACE暂停游戏");
	GetPos(78, 25);
	printf("不能撞墙和撞到自己");
	GetPos(78, 26);
	printf("夏思成制作");
}

//暂停游戏
void pause(pSnake ps)
{
	do
	{
		Sleep(300);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
		if (KEY_PRESS(VK_ESCAPE))//如果在暂停期间按esc也可以退出游戏
		{
			//退出
			ps->_curstate = EXIT_NORMAL;
			break;
		}
	} while (1);
}

//下一个位置是不是食物
int IsFood(pSnake ps, pSnakeNode node)
{
	if (ps->_pFood->x == node->x && ps->_pFood->y == node->y)
		return 1;
	return 0;
}

//吃食物
void EatFood(pSnake ps, pSnakeNode node)
{
	//头插法，将新节点连接到链表中
	node->next = ps->_pSnake;
	ps->_pSnake = node;

	//释放掉食物节点
	free(ps->_pFood);
	ps->_pFood = NULL;

	//打印蛇
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		GetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//吃了食物分数就要增加
	ps->_score += ps->_foodwelth;

	//重新创建食物
	FoodInit(ps);
}

//不是食物
void NOFood(pSnake ps, pSnakeNode node)
{
	//不是食物，现将新节点作为蛇头，在释放掉原来的尾
	//头插
	node->next = ps->_pSnake;
	ps->_pSnake = node;

	pSnakeNode cur = ps->_pSnake;
	while (cur->next->next)
	{
		GetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	GetPos(cur->next->x, cur->next->y);
	printf("  ");
	//现在cur处于倒数第二个节点，要删除倒数第一个节点
	free(cur->next);
	cur->next = NULL;
}

//撞墙
void KillByWall(pSnake ps)
{
	//蛇的坐标是否与墙冲突
	pSnakeNode cur = ps->_pSnake;
	if (cur->x == 0 ||
		cur->x == 68 ||
		cur->y == 0 ||
		cur->y == 39)
	{
		ps->_curstate = KILL_BY_WALL;
		return;
	}
	else
	{
		return;
	}
}

//撞自己
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;
	while (cur)
	{
		if (cur->x == ps->_pSnake->x &&
			cur->y == ps->_pSnake->y)
		{
			ps->_curstate = kILL_BY_SELF;
			return;
		}
		cur = cur->next;
	}
}

void SnakeMove(pSnake ps)//蛇的移动
{
	//创建一个节点
	pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (node == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}

	//先判断蛇往哪走，以此确定下一个节点的坐标
	switch (ps->_dir)
	{
		case UP:
		{
			node->x = ps->_pSnake->x;
			node->y = ps->_pSnake->y - 1;
			break;
		}
		case DOWN:
		{
			node->x = ps->_pSnake->x;
			node->y = ps->_pSnake->y + 1;
			break;
		}
		case LEFT:
		{
			node->x = ps->_pSnake->x - 2;
			node->y = ps->_pSnake->y;
			break;
		}
		case RIGHT:
		{
			node->x = ps->_pSnake->x + 2;
			node->y = ps->_pSnake->y;
			break;
		}
	}
	//下一个位置是不是食物
	if (IsFood(ps, node))
	{
		//是食物
		EatFood(ps,node);
	}
	else
	{
		//不是食物
		NOFood(ps, node);
	}

	KillByWall(ps);
	KillBySelf(ps);
}

//游戏运行
void GameRun(pSnake ps)
{		
	//打印帮助信息
	Print(ps);
	do
	{
		GetPos(78, 10);
		printf("总成绩为：%2d", ps->_score);
		GetPos(78, 11);
		printf("食物的分值为：%2d", ps->_foodwelth);
		//判断蛇的下一步走向
		if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
		{
			ps->_dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
		{
			ps->_dir = DOWN;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
		{
			ps->_dir = RIGHT;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
		{
			ps->_dir = LEFT;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//暂停
			pause(ps);
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//退出
			ps->_curstate = EXIT_NORMAL;
			break;
		}
		else if (KEY_PRESS(VK_SHIFT))
		{
			//加速
			//将休眠时间变短
			while (ps->_sleep > 80)
			{
				//最多加速5次
				ps->_sleep -= 30;
				ps->_foodwelth += 2;//每加速一次，食物的分值+2
				break;
			}
		}
		else if (KEY_PRESS(VK_CONTROL))
		{
			//减速
			//将休眠时间变长
			while (ps->_foodwelth > 2)
			{
				ps->_sleep += 20;
				ps->_foodwelth -= 2;//每减速一次，食物的分值 - 2
				break;
			}
		}		

		SnakeMove(ps);//蛇的移动

		Sleep(ps->_sleep);

	} while (ps->_curstate == OK);
}

//释放节点
void ReleaseNode(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
}

//游戏结束
//善后工作
void Gameover(pSnake ps)
{
	//判断游戏是怎样结束的
	if (ps->_curstate == KILL_BY_WALL)
	{
		GetPos(30, 19);
		printf("您撞墙死了");
	}
	else if (ps->_curstate == kILL_BY_SELF)
	{
		GetPos(30, 19);
		printf("您撞到自己死了");
	}
	else if (ps->_curstate == EXIT_NORMAL)
	{
		GetPos(30, 19);
		printf("退出游戏");
	}
	//释放节点
	ReleaseNode(ps);
	ps = NULL;
}